package com.ybt.communicate.tcp.accessor.impl;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;

import org.apache.log4j.Logger;

import com.ybt.communicate.core.exception.ProtocolException;
import com.ybt.communicate.tcp.accessor.IDataAccessStrategy;

public class SignAccessor implements IDataAccessStrategy {
    private static Logger logger = Logger.getLogger(SignAccessor.class);
    private String strategy;
    private String charset;
    private byte[] sign;

    private SignAccessor(String strategy, String charset) {
        this.strategy = strategy;
        this.charset = charset;
        try {
            this.sign = strategy.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            throw new RuntimeException(e);
        }
    }

    public void send(WritableByteChannel channel, byte[] data)
            throws ProtocolException {
        // TODO Auto-generated method stub
        try {
            // 写入消息体
            ByteBuffer bodyBuffer = ByteBuffer.wrap(data);
            while (bodyBuffer.hasRemaining()) {
                // buffer还没有写完(非阻塞io不一定一次写完)
                channel.write(bodyBuffer);
            }

            // 写入结束符
            ByteBuffer tailBuffer = ByteBuffer.wrap(sign);
            while (tailBuffer.hasRemaining()) {
                // buffer还没有写完(非阻塞io不一定一次写完)
                channel.write(tailBuffer);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            throw new ProtocolException(e);
        }

    }
    /* (non-Javadoc)
     * @see com.ybt.communicate.tcp.accessor.IDataAccessStrategy#send(java.nio.channels.WritableByteChannel, java.lang.String)
     */
    public void send(WritableByteChannel channel, String data)
            throws ProtocolException {
        // TODO Auto-generated method stub
        try {
            send(channel, data.getBytes(charset));
        } catch (UnsupportedEncodingException e) {
            throw new ProtocolException(e);
        }
    }
    public String getStrategy() {
        return strategy;
    }

    public void setStrategy(String strategy) {
        this.strategy = strategy;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public static SignAccessor newInstance(String strategy, String charset) {
        return new SignAccessor(strategy, charset);
    }

    public byte[] receive(ReadableByteChannel channel) throws ProtocolException {
        // TODO Auto-generated method stub
        InputStream in = new BufferedInputStream(Channels
                .newInputStream(channel));
        ByteArrayOutputStream result = new ByteArrayOutputStream();

        byte[] receBuffer = new byte[sign.length];
        try {
            int readLength = sign.length;
            int length = 0;
            while ((length = in.read(receBuffer, 0, readLength)) != -1) {
                // 还没达到结束符
                if (length == 0)
                    continue;

                // 下次读取的字节数
                int nextLength = nextReadLength(receBuffer, readLength);

                if (nextLength == 0) {
                    // 到达结束符
                    break;
                }

                if(readLength !=  sign.length){
                    //上次遗留了部分结束符
                    // 保存上次遗留结束符
                    result.write(receBuffer, readLength, sign.length - readLength);
                }
                
                // 保存本次有效字节数据
                result.write(receBuffer, 0, length - sign.length
                        + nextLength);

                // 下次填充数据的偏移量
                readLength = nextLength;

            }
            if (length == -1) {
                // 字节流没有碰到结束符就完了
                throw new ProtocolException("the boy don't end with the sign ["
                        + strategy + "] ");
            }

            return result.toByteArray();

        } catch (IOException e) {
            throw new ProtocolException(e);
        }
    }

    public int nextReadLength(byte[] newContent, int length) {
        // 最多比较newContent.length
        int offset = 0;
        for (; offset < length; offset++) {

            // 和sign的前newContent.length-offset进行比较
            for (int i = 0; i < length - offset; i++) {
                if (sign[i] != newContent[i + offset])
                    // 和sign的对应位不相同，跳出循环，偏移量递增
                    break;
                if (i == length - offset - 1) {
                    // newContent的后几位与sign的前几位相同
                    // newContent的后几位是部分结束符
                    return sign.length - length + offset;
                }
            }
        }
        // newContent的后几位都不是部分结束符
        return sign.length;
    }

    public static void main(String[] args) {
        /*
         * SignAccessor acc = new SignAccessor("abc", Charset.defaultCharset());
         * System.out.println(acc.nextReadLength("a".getBytes(Charset
         * .defaultCharset()),0,1));
         * System.out.println(acc.nextReadLength("ab".getBytes(Charset
         * .defaultCharset()),0,2));
         * System.out.println(acc.nextReadLength("abc".getBytes(Charset
         * .defaultCharset()),0,3));
         * System.out.println(acc.nextReadLength("b".getBytes(Charset
         * .defaultCharset()),0,1));
         * System.out.println(acc.nextReadLength("bba".getBytes(Charset
         * .defaultCharset()),0,3));
         * System.out.println(acc.nextReadLength("bab".getBytes(Charset
         * .defaultCharset()),0,3));
         */

        /*ByteArrayOutputStream in = new ByteArrayOutputStream();
        WritableByteChannel source = Channels.newChannel(in); //
        IDataAccessStrategy acc = SignAccessor.newInstance("aab", Charset
                .defaultCharset().name());
        try {
            acc.send(source, "1234aaddadasdf55".getBytes());
            System.out.println(new String(in.toByteArray(), Charset
                    .defaultCharset().name()));
        } catch (ProtocolException e) { // TODO Auto-generated catch block
            e.printStackTrace();
        }
        ReadableByteChannel target = Channels
                .newChannel(new ByteArrayInputStream(in.toByteArray()));
        try {
            System.out.println(new String(acc.receive(target), Charset
                    .defaultCharset().name()));
        } catch (ProtocolException e) { // TODO Auto-generated catch block
            e.printStackTrace();
        }*/

    }

}
